home *** CD-ROM | disk | FTP | other *** search
/ Visual Cafe 3 / Visual Cafe 3.ISO / Vcafe / JFC.bin / SystemEventQueueUtilities.java < prev    next >
Text File  |  1998-06-30  |  26KB  |  804 lines

  1. /*
  2.  * @(#)SystemEventQueueUtilities.java    1.18 98/06/09
  3.  *
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  *
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  *
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  *
  19.  */
  20. package com.sun.java.swing;
  21.  
  22. import java.awt.*;
  23. import java.awt.event.*;
  24. import java.awt.image.*;
  25.  
  26. import java.util.Hashtable;
  27. import java.util.Enumeration;
  28. import java.util.Vector;
  29.  
  30. import java.lang.reflect.InvocationTargetException;
  31.  
  32.  
  33. /**
  34.  * Swing internal utilities for dealing with the AWT system event 
  35.  * queue.  Three methods are exported, see the individual method javadoc
  36.  * for more information: addRunnableCanvas(), removeRunnableCanvas(), 
  37.  * postRunnable(), queueComponentWorkRequest().
  38.  * 
  39.  * @see RepaintManager
  40.  * @see JRootPane
  41.  */
  42. class SystemEventQueueUtilities
  43. {
  44.     private static boolean checkedSystemEventQueue = false;
  45.     private static EventQueue systemEventQueue = null;
  46.     private static Hashtable rootTable = new Hashtable(4);
  47.  
  48.     /**
  49.      * A Runnable with a component.  If we need to post this
  50.      * runnable to the AWT system event queue, we'll find it's
  51.      * JRootPane ancestor and use that as the key to the table
  52.      * of RunnableCanvas's.
  53.      * <p>
  54.      * Extended by RepaintManager.WorkRequest()
  55.      * 
  56.      * @see RunnableCanvas
  57.      */
  58.     private static class ComponentWorkRequest implements Runnable 
  59.     {
  60.     boolean isPending;
  61.     Component component;
  62.  
  63.     ComponentWorkRequest(Component c) {
  64.         component = c;
  65.     }
  66.  
  67.     public void run() {
  68.         RepaintManager rm;
  69.         synchronized (this) {
  70.         rm = RepaintManager.currentManager(component);
  71.         isPending = false;
  72.         }
  73.         rm.validateInvalidComponents();
  74.         rm.paintDirtyRegions();
  75.     }
  76.     }
  77.  
  78.  
  79.     /**
  80.      * This method is used by RepaintManager to queue a ComponentWorkRequest
  81.      * with invokeLater().  It assumes that the root argument is either
  82.      * and Applet or a Window, see SwingUtilities.getRoot().
  83.      */
  84.     static void queueComponentWorkRequest(Component root) 
  85.     {
  86.     ComponentWorkRequest req = (ComponentWorkRequest)(rootTable.get(root));
  87.     boolean newWorkRequest = (req == null);
  88.     if (newWorkRequest) {
  89.         req = new ComponentWorkRequest(root);
  90.     }
  91.  
  92.     /* At this point the ComponentWorkRequest may be accessible from
  93.      * an event dispatching thread so before updating it further
  94.      * we synchronize access to it.
  95.      */
  96.     synchronized(req) {
  97.         if (newWorkRequest) {
  98.         rootTable.put(root, req);
  99.         }
  100.         if (!req.isPending) {
  101.         SwingUtilities.invokeLater(req);
  102.         req.isPending = true;
  103.         }
  104.     }
  105.     }
  106.  
  107.  
  108.     /**
  109.      * Associate a RunnableCanvas and a JRootPane to enable queuing 
  110.      * events for the root pane's parent window's event dispatching thread.
  111.      * Adds a 1x1 RunnableCanvas to the root pane's layered pane.
  112.      * <p>
  113.      * Called by JRootPane.addNotify() to set up the RunnableCanvas.
  114.      * 
  115.      * @see RunnableCanvas
  116.      * @see JRootPane#addNotify
  117.      */
  118.     static synchronized void addRunnableCanvas(JRootPane rootPane) 
  119.     {
  120.     /* This embarrassment is neccessary to avoid the unconditional console
  121.      * error message produced by both Navigator and IE4 when an applet
  122.      * looks up the system event queue.  Applets that want to avoid
  123.      * generating the one time error message (see initSystemEventQueue()
  124.      * below) should set this property.  Hopefully future versions
  125.      * of the browsers will not have this limitation and we'll be 
  126.      * able to retire this hack.
  127.      */
  128.     if (rootPane.getClientProperty("defeatSystemEventQueueCheck") != null) {
  129.         checkedSystemEventQueue = true;
  130.     }
  131.  
  132.     /* We don't bother with the RunnableCanvas if the app has
  133.          * access to the system event queue.
  134.      */
  135.     if(!checkedSystemEventQueue)
  136.         initSystemEventQueue();
  137.  
  138.     if(systemEventQueue != null)
  139.         return;
  140.  
  141.     JLayeredPane layeredPane = rootPane.getLayeredPane();
  142.     if(layeredPane != null) {
  143.         RunnableCanvas rc = new RunnableCanvas(rootPane);
  144.         layeredPane.add(rc);
  145.     }
  146.     }
  147.     
  148.  
  149.     /**
  150.      * Remove the RunnableCanvas from the JRootPane and clear the
  151.      * internal bookeeping associated with it.
  152.      * <p>
  153.      * Called by JRootPane.removeNotify() 
  154.      * 
  155.      * @see RunnableCanvas
  156.      */
  157.     static synchronized void removeRunnableCanvas(JRootPane rootPane) {
  158.     rootTable.remove(SwingUtilities.getRoot(rootPane));
  159.     RunnableCanvas.remove(rootPane);
  160.     }
  161.  
  162.  
  163.     /**
  164.      * Post an event to the AWT System event queue that, when dispatched,
  165.      * will invoke the specified Runnable.  If lock is non-null this call
  166.      * blocks (by waiting on the lock) until the doRun() method returns, 
  167.      * otherwise we return as soon as the event has been enqueued.  An
  168.      * exception is only returned if lock is non-null, i.e. if we're
  169.      * being called from invokeAndWait().
  170.      * <p>
  171.      * This method is only intended to support SwingUtilities.invokeLater()
  172.      * and SwingUtilities.invokeAndWait().
  173.      */
  174.     static Exception postRunnable(Runnable doRun, Object lock) 
  175.     {
  176.     /* Don't unconditionally call initSystemEventQueue() here to 
  177.      * avoid the cost of calling a synchronized method.
  178.      */
  179.     if (!checkedSystemEventQueue) {
  180.         initSystemEventQueue();
  181.     }
  182.  
  183.     RunnableEvent event = new RunnableEvent(doRun, lock);
  184.     if (systemEventQueue != null) {
  185.         systemEventQueue.postEvent(event);
  186.     }
  187.     else {
  188.         postRunnableCanvasEvent(event);
  189.     }
  190.     return event.exception;
  191.     }
  192.  
  193.  
  194.     /**
  195.      * Adds a RunnableEvent to all the remaining RunnableCanvases to restart
  196.      * the TimerQueues thread.
  197.      *
  198.      * @see RunnableCanvas#postRunnableEventToAll
  199.      */
  200.     static synchronized void restartTimerQueueThread() {
  201.     // This only valid for Applets.
  202.     if (systemEventQueue == null) {
  203.         Runnable restarter = new TimerQueueRestart();
  204.         RunnableEvent event = new RunnableEvent(restarter, null);
  205.         RunnableCanvas.postRunnableEventToAll(event);
  206.     }
  207.     }
  208.  
  209.  
  210.     /**
  211.      * Runnable that will message the shared instance of the Timer Queue
  212.      * to restart.
  213.      *
  214.      * @see #restartTimerQueueThread
  215.      */
  216.     private static class TimerQueueRestart implements Runnable {
  217.     boolean attemptedStart;
  218.  
  219.     public synchronized void run() {
  220.         // Only try and restart the q once.
  221.         if(!attemptedStart) {
  222.         TimerQueue q = TimerQueue.sharedInstance();
  223.  
  224.         synchronized(q) {
  225.             if(!q.running)
  226.             q.start();
  227.         }
  228.         attemptedStart = true;
  229.         }
  230.     }
  231.     }
  232.  
  233.     /**
  234.      * Event type used for dispatching runnable objects for
  235.      * SwingUtilities.invokeLater() and SwingUtilities.invokeAndWait().
  236.      * 
  237.      * @see #postRunnable
  238.      */
  239.     private static class RunnableEvent extends AWTEvent {
  240.         static final int EVENT_ID = AWTEvent.RESERVED_ID_MAX + 1000;
  241.     static final Component target = new RunnableTarget();
  242.     final Runnable doRun;
  243.     final Object lock;
  244.     Exception exception;
  245.  
  246.         RunnableEvent(Runnable doRun, Object lock) {
  247.             super(target, EVENT_ID);
  248.         this.doRun = doRun;
  249.         this.lock = lock;
  250.         }
  251.     }
  252.  
  253.  
  254.     /**
  255.      * If the AWT system event queue is accessible, cache it and set a flag
  256.      * indicating that we've checked.
  257.      * <p>
  258.      * This method only applies to applications.  Browsers as of January 1998 do 
  259.      * not provide access to the AWT system event queue.
  260.      */
  261.     private static synchronized void initSystemEventQueue() {
  262.     if (!checkedSystemEventQueue) {
  263.         try {
  264.         systemEventQueue = Toolkit.getDefaultToolkit().getSystemEventQueue();
  265.         }
  266.         catch(SecurityException e) {
  267.         System.err.println("Swing: checked access to system event queue.");
  268.         }
  269.         finally {
  270.         checkedSystemEventQueue = true;
  271.         }
  272.     }
  273.     }
  274.  
  275.  
  276.     /**
  277.      * Calls RunnableEvent.doRun.run().  If RunnableEvent.lock is non
  278.      * null then we synchronize the run() call and save the exception
  279.      * (if any) in the RunnableEvent.exception field.
  280.      */
  281.     private static void processRunnableEvent(RunnableEvent runnableEvent)
  282.     {
  283.     Object lock = runnableEvent.lock;
  284.     if (lock == null) {
  285.         runnableEvent.doRun.run();
  286.     }
  287.     else {
  288.         synchronized(lock) {
  289.         try {
  290.             runnableEvent.doRun.run();
  291.         }
  292.         catch (Exception e) {
  293.             runnableEvent.exception = e;
  294.         }
  295.         finally {
  296.             if (runnableEvent.lock != null) {
  297.             runnableEvent.lock.notify();
  298.             }
  299.         }
  300.         }
  301.     }
  302.     }
  303.  
  304.  
  305.     /**
  306.      * A dummy Component subclass that (only) handles RunnableEvents.  If the
  307.      * AWT System event queue is accessible (i.e. we're running as
  308.      * an application or as trusted code), RunnableEvents are dispatched
  309.      * to this component.
  310.      * 
  311.      * @see #processRunnableEvent
  312.      */
  313.     private static class RunnableTarget extends Component 
  314.     {
  315.         RunnableTarget() {
  316.             super();
  317.             enableEvents(RunnableEvent.EVENT_ID);
  318.         }
  319.  
  320.         protected void processEvent(AWTEvent event) {
  321.         if (event instanceof RunnableEvent) {
  322.         processRunnableEvent((RunnableEvent)event);
  323.         }
  324.         }
  325.     }
  326.  
  327.  
  328.     /**
  329.      * Synchronized entry point to the applet support for AWT System 
  330.      * event queue access.  This method adds the event to the appropriate
  331.      * runnable canvas's queue and then has the canvas repaint().  Note 
  332.      * that by the time the event dispatching thread gets to handling
  333.      * the repaint() (by calling runnableCanvas.update()), many runnable
  334.      * events may have been queued up.
  335.      * 
  336.      * @see RunnableCanvas#addRunnableEvent
  337.      * @see RunnableCanvas#update
  338.      */
  339.     private static synchronized void postRunnableCanvasEvent(RunnableEvent e) {
  340.     RunnableCanvas runnableCanvas = RunnableCanvas.lookup(e);
  341.  
  342.     if (runnableCanvas == null) {
  343.  
  344.         /* If this is a ComponentWorkRequest and we were unable to 
  345.          * queue it, then clear the pending flag.
  346.          */
  347.         if (e.doRun instanceof ComponentWorkRequest) {
  348.         ComponentWorkRequest req = (ComponentWorkRequest)e.doRun;
  349.             synchronized(req) {
  350.             req.isPending = false;
  351.         }
  352.         // new Exception("RunnableCanvas.lookup(e) failed: " + req.component).printStackTrace();
  353.         }
  354.  
  355.         // If this is a Timer event let it know that it didn't fire.
  356.         if(e.doRun instanceof Timer.DoPostEvent) {
  357.         ((Timer.DoPostEvent)e.doRun).getTimer().eventQueued = false;
  358.         }
  359.  
  360.         /* We are unable to queue this event on a system event queue.  Make
  361.          * sure that any code that's waiting for the runnable to finish
  362.          * doesn't hang.
  363.          */
  364.         if (e.lock != null) {
  365.         e.lock.notify();
  366.         }
  367.         return;
  368.     }
  369.  
  370.     runnableCanvas.addRunnableEvent(e);
  371.     runnableCanvas.repaint();
  372.     }
  373.  
  374.  
  375.     /** 
  376.      * Applets don't have direct access to the AWT SystemEvent queue.  To 
  377.      * work around this we call RunnableCanvas.repaint() on a per applet 
  378.      * instance of this class.  The AWT deals with this by queuing a 
  379.      * java.awt.PaintEvent for the event dispatching thread which 
  380.      * is dispatched (Component.dispatchEvent()) the usual way.
  381.      * Component.dispatchEvent() handles PaintEvents by calling our update()
  382.      * method (on the event dispatching thread) which processes
  383.      * the RunnableEvents stashed in the runnableEvents vector.
  384.      */
  385.      private static class RunnableCanvas extends Canvas
  386.      {
  387.      private static final Graphics nullGraphics = new RunnableCanvasGraphics();
  388.      private static Hashtable runnableCanvasTable = new Hashtable(1);
  389.      private Vector runnableEvents = new Vector(2);
  390.      private boolean isRegistered = false;
  391.  
  392.      RunnableCanvas(JRootPane rootPane) {
  393.          super();
  394.          setBounds(0, 0, 1, 1);
  395.  
  396.          /* Remember the mapping from the current thread (and the current 
  397.           * thread group) to this RunnableCanvas.  Note that if a mapping
  398.           * has already been defined, e.g. this rootPane belongs to an 
  399.           * existing applet, then leave the table alone.  We're assuming that
  400.           * an applets addNotify method will always run before the addNotify
  401.           * method in any subsidiary windows the applet creates can run.
  402.           */
  403.          if (runnableCanvasTable.get(Thread.currentThread()) == null) {
  404.          try {
  405.              runnableCanvasTable.put(Thread.currentThread(), this);
  406.              /* IE4.0 throws a SecurityException if you apply getThreadGroup()
  407.               * to the event dispatching thread.  However a child of the 
  408.               * event dispatching thread (same thread group) is OK.
  409.               */
  410.              runnableCanvasTable.put(new Thread().getThreadGroup(), this);
  411.              if (SwingUtilities.isEventDispatchThread()) {
  412.              isRegistered = true;
  413.              }
  414.          } 
  415.          catch(Exception e) {
  416.              System.err.println("Can't register RunnableCanvas");
  417.              e.printStackTrace();
  418.          }
  419.          }
  420.          runnableCanvasTable.put(rootPane, this);
  421.          maybeRegisterEventDispatchThread();
  422.      }
  423.  
  424.  
  425.      /**
  426.       * If called on an event dispatching thread that we haven't seen
  427.       * before then make two hashtable entries in the runnableCanvasTable:
  428.       * <pre>
  429.       *   current thread => this RunnableCanvas
  430.       *   current thread group => this RunnableCanvas
  431.       * </pre>
  432.       * @see #lookup
  433.       */
  434.      private void maybeRegisterEventDispatchThread() {
  435.          /* Avoid the cost of a synchronized block (or method) in the 
  436.           * common case, since this method is called each time paint is called.
  437.           */
  438.          if (!isRegistered) {
  439.          synchronized(this) {
  440.              if (!isRegistered && SwingUtilities.isEventDispatchThread()) {
  441.              Thread currentThread = Thread.currentThread();
  442.  
  443.              /* If this event dispatching thread is already mapped to 
  444.               * a runnableCanvas then don't replace the mapping (which
  445.               * we expect to be generated by the applet).
  446.               */
  447.              if (runnableCanvasTable.get(currentThread) != null) {
  448.                  isRegistered = true;
  449.              }
  450.              else {
  451.                  runnableCanvasTable.put(currentThread, this);
  452.                  /* IE4.0 throws a SecurityException if you apply getThreadGroup()
  453.                   * to the event dispatching thread.  However a child of the 
  454.                   * event dispatching thread (same thread group) is OK.
  455.                   */
  456.                  runnableCanvasTable.put(new Thread().getThreadGroup(), this);
  457.                  isRegistered = true;
  458.              }
  459.              }
  460.          }
  461.          }
  462.      }
  463.  
  464.  
  465.      /**
  466.       * If we're running on the event dispatching thread then lookup 
  467.       * the canvas with the current thread itself, otherwise use the 
  468.       * current threads thread group.  Note that IE4.0 throws a security
  469.       * exception if you apply getThreadGroup() to a system thread, e.g. 
  470.       * the event dispatching thread or an image observer thread.<p>
  471.       * If there is no match for the ThreadGroup, the first showing
  472.       * RunnableCanvas is returned.
  473.       */
  474.      static RunnableCanvas lookup(RunnableEvent e) {
  475.          /* If this is a ComponentWorkRequest, find the components 
  476.           * JRootPane ancestor and use that as the index into the 
  477.           * runnableCanvasTable.  This case occurs when any thread,
  478.           * other than the event dispatching thead, calls repaint
  479.           */
  480.          if (e.doRun instanceof ComponentWorkRequest) {
  481.          ComponentWorkRequest req = (ComponentWorkRequest)e.doRun;
  482.          synchronized(req) {
  483.              JRootPane rootPane = SwingUtilities.getRootPane(req.component);
  484.              if(rootPane != null) {
  485.              return (RunnableCanvas)(runnableCanvasTable.get(rootPane));
  486.              }
  487.              /* Failure.  There doesn't appear to be a RunnableCanvas to use
  488.               * so indicate that a new request will need to be queued, see
  489.               * RepaintManager.queueWorkRequest().
  490.               */
  491.              req.isPending = false;
  492.              return null;
  493.          }
  494.          }
  495.  
  496.          /* If the current thread is in the runnableCanvasTable
  497.           * (e.g. we're on the event dispatching thread) we're done.
  498.           */
  499.          Object rv = runnableCanvasTable.get(Thread.currentThread());
  500.          if (rv != null) {
  501.          return (RunnableCanvas)rv;
  502.          }
  503.  
  504.          /* At this point we're assuming that the calling thread isn't
  505.           * a system thread, so it's safe to lookup via the current threads
  506.           * ThreadGroup.
  507.           */
  508.          Object threadGroup;
  509.          try {
  510.          threadGroup = Thread.currentThread().getThreadGroup();
  511.          } 
  512.          catch (SecurityException exc) {
  513.          return null;
  514.          }
  515.          RunnableCanvas rc = (RunnableCanvas)runnableCanvasTable.
  516.                                          get(threadGroup);
  517.          if(rc == null) {
  518.          // No RunnableCanvas, try and find the first visible canvas.
  519.          Enumeration keys = runnableCanvasTable.keys();
  520.          if(keys == null)
  521.              return null;
  522.          while(keys.hasMoreElements()) {
  523.              Object key = keys.nextElement();
  524.              if((key instanceof JRootPane) &&
  525.             ((JRootPane)key).isShowing())
  526.              return (RunnableCanvas)runnableCanvasTable.get(key);
  527.          }
  528.          }
  529.          return rc;
  530.      }
  531.  
  532.  
  533.      /**
  534.       * Adds the event to all the RunnableCanvases.
  535.       *
  536.       * @see #restartTimerQueueThread
  537.       */
  538.      static void postRunnableEventToAll(RunnableEvent e) {
  539.          // Determine the RunnableCanvas for the current thread. It
  540.          // may be null.
  541.          RunnableCanvas currentThreadCanvas;
  542.          ThreadGroup tg;
  543.          try {
  544.          tg = new Thread().getThreadGroup();
  545.          }
  546.          catch (SecurityException se) {
  547.          tg = null;
  548.          }
  549.          if(tg != null) {
  550.          currentThreadCanvas = (RunnableCanvas)runnableCanvasTable.
  551.                                 get(tg);
  552.          }
  553.          else
  554.          currentThreadCanvas = null;
  555.  
  556.          // Add the event to all canvases, except the current one.
  557.          // Presumably the current one is no longer valid and will be
  558.          // going away shortly.
  559.          Enumeration keys = runnableCanvasTable.keys();
  560.          while(keys.hasMoreElements()) {
  561.          Object key = keys.nextElement();
  562.          if(key instanceof JRootPane) {
  563.              Object canvas = runnableCanvasTable.get(key);
  564.              if(canvas != currentThreadCanvas) {
  565.              RunnableCanvas rc = (RunnableCanvas)canvas;
  566.              rc.addRunnableEvent(e);
  567.              rc.repaint();
  568.              }
  569.          }
  570.          }
  571.      }
  572.  
  573.  
  574.      /**
  575.       * Remove the RunnableCanvas associated with this applet from the
  576.       * applets Layered pane and clear all of the runnableCanvasTable
  577.       * entries that point at it.
  578.       */
  579.      static void remove(JRootPane rootPane) {
  580.          RunnableCanvas rc = (RunnableCanvas)(runnableCanvasTable.get(rootPane));
  581.          if (rc != null) {
  582.          RunnableCanvas nextCanvas = null;
  583.          JLayeredPane layeredPane = rootPane.getLayeredPane();
  584.          layeredPane.remove((Component)rc);
  585.  
  586.          Enumeration keys = runnableCanvasTable.keys();
  587.          while(keys.hasMoreElements()) {
  588.              Object key = keys.nextElement();
  589.              Object next = runnableCanvasTable.get(key);
  590.              if (rc == next) {
  591.              runnableCanvasTable.remove(key);
  592.              }
  593.              else if(nextCanvas == null) {
  594.              nextCanvas = (RunnableCanvas)next;
  595.              }
  596.          }
  597.  
  598.          // If there are still events, either move them to another
  599.          // canvas, or mark the Timer type events as not having
  600.          // fired.
  601.          RunnableEvent[] events = rc.getRunnableCanvasEvents();
  602.          int numEvents = (events == null) ? 0 : events.length;
  603.          if(numEvents > 0) {
  604.              if(nextCanvas != null) {
  605.              for(int counter = 0; counter < numEvents; counter++) {
  606.                  RunnableEvent e = events[counter];
  607.                  if(e.doRun instanceof Timer.DoPostEvent)
  608.                  nextCanvas.addRunnableEvent(e);
  609.              }
  610.              nextCanvas.repaint();
  611.              }
  612.              else {
  613.              // Mark all Timer type event as not having fired.
  614.              for(int counter = 0; counter < numEvents; counter++) {
  615.                  RunnableEvent event = events[counter];
  616.                  if(event.doRun instanceof Timer.DoPostEvent) {
  617.                  ((Timer.DoPostEvent)event.doRun).getTimer().
  618.                                      eventQueued = false;
  619.                  }
  620.              }
  621.              }
  622.          }
  623.          }
  624.      }
  625.  
  626.  
  627.      /**
  628.       * If there are events to be processed then we're showing.  Note
  629.       * that the AWT code that dispatches paint events short circuits
  630.       * (does nothing) if isShowing() returns false.
  631.       */
  632.      public boolean isShowing() { 
  633.          return runnableEvents.size() > 0;
  634.      }
  635.  
  636.  
  637.      /**
  638.       * Reduce the cost of repainting (since we're not going to draw
  639.       * anything) by returning a constant no-op graphics object.
  640.       */
  641.      public Graphics getGraphics() {
  642.          return nullGraphics;
  643.      }
  644.      
  645.  
  646.      /**
  647.       * Testing purposes only.  This method shouldn't be called;
  648.       * the parent of this component should have a null layout
  649.       * manager.
  650.       */
  651.      public Dimension getPreferredSize() {
  652.          return new Dimension(1, 1);
  653.      }
  654.  
  655.  
  656.      /**
  657.       * Add a RunnableEvent to the queue that will be dispatched
  658.       * when this component is repainted.
  659.       * @see #update
  660.       */
  661.      synchronized void addRunnableEvent(RunnableEvent e) {
  662.          runnableEvents.addElement(e); 
  663.      }
  664.  
  665.  
  666.      /**
  667.       * Return an (array) copy of the runnableEvents vector or
  668.       * null if the vector is empty.  The update method processes
  669.       * a copy of the vector so that we don't have to hold
  670.       * the synchronized lock while calling processRunnableEvent().
  671.       * @see #update
  672.       */
  673.      private synchronized RunnableEvent[] getRunnableCanvasEvents() {
  674.          int n = runnableEvents.size();
  675.          if (n == 0) {
  676.          return null;
  677.          }
  678.          else {
  679.          RunnableEvent[] rv = new RunnableEvent[n];
  680.          for(int i = 0; i < n; i++) {
  681.              rv[i] = (RunnableEvent)(runnableEvents.elementAt(i));
  682.          }
  683.          runnableEvents.removeAllElements();
  684.          return rv;
  685.          }
  686.      }
  687.  
  688.  
  689.      public void paint(Graphics g) {
  690.          maybeRegisterEventDispatchThread();         
  691.      }
  692.  
  693.  
  694.      /**
  695.       * Process all of the RunnableEvents that have accumulated
  696.       * since RunnableCanvas.repaint() was called.
  697.       */
  698.      public void update(Graphics g) {
  699.          RunnableEvent[] events = getRunnableCanvasEvents();
  700.          if (events != null) {
  701.          for(int i = 0; i < events.length; i++) {
  702.              processRunnableEvent(events[i]);
  703.          }
  704.          }
  705.      }
  706.     }
  707.  
  708.  
  709.     /**
  710.      * A no-op Graphics object for the RunnableCanvas component.
  711.      * Most AWT Component implementations handle update events 
  712.      * like this:
  713.      * <pre>
  714.      *      Graphics g = getGraphics();
  715.      *      Rectangle r = ((PaintEvent)e).getUpdateRect();
  716.      *      g.clipRect(r.x, r.y, r.width, r.height);
  717.      *      update(g)
  718.      *      g.dispose();
  719.      * </pre>
  720.      * Since the RunnableCanvas component isn't really going to do
  721.      * any painting we don't bother with creating/disposing real
  722.      * graphics objects, or setting any of the properties.
  723.      * 
  724.      */
  725.     private static class RunnableCanvasGraphics extends Graphics
  726.     {
  727.     public Graphics create() { 
  728.         return this;
  729.     }
  730.  
  731.     /* We don't expect any of the following methods to be called but 
  732.      * we still return marginally valid values for the get methods 
  733.      * just in case.
  734.      */
  735.  
  736.     public Rectangle getClipBounds() { 
  737.         return new Rectangle(0, 0, Short.MAX_VALUE, Short.MAX_VALUE);
  738.     }
  739.  
  740.     public Shape getClip() { 
  741.         return (Shape)(getClipBounds());
  742.     }
  743.  
  744.     public  void dispose() {}
  745.     public void translate(int x, int y) {}
  746.     public Color getColor() { return Color.black; }
  747.     public void setColor(Color c) {} 
  748.     public void setPaintMode() {}
  749.     public void setXORMode(Color c) {}
  750.     public Font getFont() { return null; }
  751.     public void setFont(Font font) {}
  752.     public FontMetrics getFontMetrics(Font f) { return null; }
  753.     public void clipRect(int x, int y, int width, int height) {}
  754.     public void setClip(int x, int y, int width, int height) {}
  755.     public void setClip(Shape clip) {}
  756.     public void copyArea(int x, int y, int w, int h, int dx, int dy) {}
  757.     public void drawLine(int x1, int y1, int x2, int y2) {}
  758.     public void fillRect(int x, int y, int width, int height) {}
  759.     public void clearRect(int x, int y, int width, int height) {}
  760.     public void drawRoundRect(int x, int y, int w, int h, int aw, int ah) {}
  761.     public void fillRoundRect(int x, int y, int w, int h, int aw, int ah) {}
  762.     public void drawOval(int x, int y, int w, int h) {}
  763.     public void fillOval(int x, int y, int w, int h) {}
  764.     public void drawArc(int x, int y, int w, int h, int sa, int aa) {}
  765.     public void fillArc(int x, int y, int w, int h, int sa, int aa) {}
  766.     public void drawPolyline(int xPoints[], int yPoints[], int nPoints) {}
  767.     public void drawPolygon(int xPoints[], int yPoints[], int nPoints) {}
  768.     public void fillPolygon(int xPoints[], int yPoints[], int nPoints) {}
  769.     public void drawString(String str, int x, int y) {}
  770.         /*if[JDK1.2]
  771.         public void drawString(java.text.AttributedCharacterIterator iterator, int x, int y) {}
  772.          end[JDK1.2]*/
  773.     public boolean drawImage(Image i, int x, int y, ImageObserver o) { return false; }
  774.     public boolean drawImage(Image i, int x, int y, int w, int h, ImageObserver o) { return false; }
  775.     public boolean drawImage(Image i, int x, int y, Color bgcolor, ImageObserver o) { return false; }
  776.     public boolean drawImage(Image i, int x, int y, int w, int h, Color c, ImageObserver o) { return false; }
  777.     public boolean drawImage(Image i, 
  778.             int dx1, int dy1, int dx2, int dy2, 
  779.         int sx1, int sy1, int sx2, int sy2, ImageObserver o) 
  780.         { return false; }
  781.     public boolean drawImage(Image i, 
  782.             int dx1, int dy1, int dx2, int dy2, 
  783.         int sx1, int sy1, int sx2, int sy2, Color c, ImageObserver o)
  784.         { return false; }
  785.     }
  786.  
  787.  
  788.     /* Print a warning if we're running the wrong version of this class for 
  789.      * this JDK.
  790.      */
  791.     static {
  792.         /*if[JDK1.2]
  793.           if (!SwingUtilities.is1dot2) {
  794.               System.err.println("warning: running 1.2 version of SystemEventQueueUtilities");
  795.           }
  796.           else[JDK1.2]*/
  797.           if (SwingUtilities.is1dot2) {
  798.               System.err.println("warning: running 1.1 version of SystemEventQueueUtilities");
  799.           }
  800.         /*end[JDK1.2]*/
  801.     }
  802. }
  803.  
  804.